#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "common.h"

#define MAX_COMMAND_LENGTH 1024
#define MAX_TOKEN_COUNT 128
#define ERROR_IN_FORK "Error in fork\n"
#define CANNOT_EXECUTE "Could not execute command '%s'\n"

//executes the command in current process
void do_execute_command(char * command) 
{
  // Args is allocated here, but I never do free(args)
  // It won't make memory leak, beacuse 
  // this process will exec or exit 
  char * * args = my_malloc(MAX_TOKEN_COUNT * sizeof(char));
  split(command, " ", args);
  if (execvp(args[0], args) < 0) {
    printf(CANNOT_EXECUTE, command);
    exit(1);
  } 
}

//executes command in new process
void execute_command(char * command) 
{
  int result = fork();
  if (result < 0) {
    printf(ERROR_IN_FORK);
    exit(1);
  }
  else if (result == 0) {
    do_execute_command(command);
  }
  else {
    wait(0);
  }
}

//executes command in separate process and attaches output to the pipe
void execute_first_command_in_pipe(int pipe_descriptors[2], char * command)
{
  int pid = fork();
  if (pid < 0) {
    printf("Unanable to fork");
    exit(1);
  }
  else if (pid == 0) {
    int result = close(pipe_descriptors[0]);
    int result2 = close(STDOUT_FILENO);
    int result3 = dup(pipe_descriptors[1]);
    if (result < 0 || result2 < 0 || result3 < 0) {
      printf("Unanable to attach process output to pipe");
    }
    do_execute_command(command);
  } 
}

//executes command in separate process and attaches input to the pipe
void execute_last_command_in_pipe(int pipe_descriptors[2], char * command)
{
  int pid = fork();
  if (pid < 0) {
    printf("Unanable to fork");
    exit(1);
  }
  else if (pid == 0) {
    int result = close(pipe_descriptors[1]);
    int result2 = close(STDIN_FILENO);
    int result3 = dup(pipe_descriptors[0]);
    if (result < 0 || result2 < 0 || result3 < 0) {
      printf("Unanable to attach process output to pipe");
    }
    do_execute_command(command);
  } 
}

//executes whole command or pipe
void execute_pipe(char * command) 
{
  char * * args = my_malloc(MAX_TOKEN_COUNT * sizeof(char));
  split(command, "|", args);
  if (args[0] != NULL) {
    if (args[1] != NULL) {
      int fd[2];

      if (pipe(fd) < 0) {
        printf("Unanable to create a pipe");
        exit(1);
      }

      execute_last_command_in_pipe(fd, args[1]);
      execute_first_command_in_pipe(fd, args[0]);
      close(fd[0]);
      close(fd[1]);
      int status;
      wait(&status);
      wait(&status);    
    } else {
      execute_command(args[0]);
    }
  }
  purge(args);
  my_free(args);
}

void execute_cd(char * command) 
{
  if (chdir(command+3) < 0)
    printf("Unanable to go to directory '%s'\n", command+3);
}

void main_loop() 
{
  char command[MAX_COMMAND_LENGTH];
  int stop = 0;
  while (!stop)
  {
    printf("] ");
    get_command(command);
    if (strcmp(command, "exit") == 0)
      stop = 1;
    else if (strncmp(command, "cd ", 3) == 0 || strcmp(command, "cd") == 0)
      execute_cd(command); 
    else
      execute_pipe(command);
  }
}

int main() 
{
  main_loop();
  my_assert_malloc_ok();
  return 0;
}
